/**
 * 玩家类
 */
class Player extends AcGameObject {
    /**
     *
     * @param {} playground
     * @param {玩家中心点x坐标} x
     * @param {玩家中心点y坐标} y
     * @param {球的半径} radius 现阶段玩家为圆形
     * @param {玩家颜色} color
     * @param {玩家速度} speed 注意这里的速度是指每次移动的在屏幕上的百分比
     * @param {是否是自己} is_me
     */
    constructor(playground, x, y, radius, color, speed, character, username, photo) {
        super();
        // 将传入的参数保存到对象中
        this.playground = playground;
        // 将ctx取出来，方便后面使用
        this.ctx = this.playground.game_map.ctx;
        // 这里是画布中的相对坐标
        this.x = x;
        this.y = y;
        this.vx = 0;
        this.vy = 0;
        this.damage_x = 0;
        this.damage_y = 0;
        this.damage_speed = 0;
        this.move_length = 0;
        this.radius = radius;
        this.color = color;
        this.speed = speed;
        this.character = character;
        this.username = username;
        this.photo = photo;
        // 如果运算的结果为小于0.1，那么将其认定为0
        this.eps = 0.01;
        this.friction = 0.9;
        this.spent_time = 0;
        this.fireballs = [];

        // 当前技能
        this.cur_skill = null;

        if (this.character !== "robot") {
            this.img = new Image();
            this.img.src = this.photo;
        }

        if (this.character === "me") {
            this.fireball_coldtime = 3; // 冷却时间3秒钟
            this.fireball_img = new Image();
            this.fireball_img.src = "https://cdn.acwing.com/media/article/image/2021/12/02/1_9340c86053-fireball.png";

            this.blink_coldtime = 5;    // 闪现冷却时间5秒
            this.blink_img = new Image();
            this.blink_img.src = "https://cdn.acwing.com/media/article/image/2021/12/02/1_daccabdc53-blink.png";
        }
    }

    start() {
        this.playground.player_count ++;
        this.playground.notice_board.write("已就绪：" + this.playground.player_count + "人");

        if (this.playground.player_count >= 3) {
            this.playground.state = "fighting";
            this.playground.notice_board.write("Fighting");
        }

        // 如果是自己，那么就添加监听事件
        if (this.character === "me") {
            this.add_listening_events();
        } else if (this.character === "robot") {    // 如果是机器人，那么就随机移动
            let tx = Math.random() * this.playground.width / this.playground.scale;
            let ty = Math.random() * this.playground.height / this.playground.scale;
            this.move_to(tx, ty);
        }
    }

    add_listening_events() {
        let outer = this;
        // 将右键点击事件取消
        this.playground.game_map.$canvas.on("contextmenu", function() {
            return false;
        });
        // 监听鼠标点击事件
        this.playground.game_map.$canvas.mousedown(function(e) {
            if (outer.playground.state !== "fighting")
                // return false会将事件阻断
                return true;

            const rect = outer.ctx.canvas.getBoundingClientRect();
            // 如果是鼠标右键点击
            if (e.which === 3) {
                // 将绝对坐标转为相对坐标
                let tx = (e.clientX - rect.left) / outer.playground.scale;
                let ty = (e.clientY - rect.top) / outer.playground.scale;
                outer.move_to(tx, ty);

                if (outer.playground.mode === "multi mode") {
                    outer.playground.mps.send_move_to(tx, ty);
                }
            } else if (e.which === 1) { // 如果是鼠标左键点击
                let tx = (e.clientX - rect.left) / outer.playground.scale;
                let ty = (e.clientY - rect.top) / outer.playground.scale;
                // 如果当前技能是fireball
                if (outer.cur_skill === "fireball") {
                    if (outer.fireball_coldtime > outer.eps)
                        return false;
                    // 调用shoot_fireball方法
                    // 将绝对坐标转为相对坐标
                    let fireball = outer.shoot_fireball(tx, ty);

                    if (outer.playground.mode === "multi mode") {
                        outer.playground.mps.send_shoot_fireball(tx, ty, fireball.uuid);
                    }
                } else if (outer.cur_skill === "blink") {
                    if (outer.blink_coldtime > outer.eps)
                        return false;

                    outer.blink(tx, ty);

                    if (outer.playground.mode === "multi mode") {
                        outer.playground.mps.send_blink(tx, ty);
                    }
                }
                // 清空当前技能
                outer.cur_skill = null;
            }
        });

        // 监听键盘事件
        // 修改监听的对象，由window变为$canvas
        this.playground.game_map.$canvas.keydown(function(e) {
            // 回车
            if (e.which === 13) {
                // 打开聊天框
                if (outer.playground.mode === "multi mode") {
                    outer.playground.chat_field.show_input();
                    return false;
                }
            } else if (e.which === 27) {
                // esc键
                // 关闭聊天框
                if (outer.playground === "multi mode") {
                    outer.playground.chat_field.hide_input();
                }
            }

            if (outer.playground.state !== "fighting")
                return true;
            // 如果是q键
            if (e.which === 81) {
                if (outer.fireball_coldtime > outer.eps)
                    return true;
                // 设置当前技能为fireball
                outer.cur_skill = "fireball";
                return false;
            } else if (e.which === 70) {
                if (outer.blink_coldtime > outer.eps)
                    return true;
                outer.cur_skill = "blink";
                return false;
            }
        })
    }

    // 发射火球
    shoot_fireball(tx, ty) {
        // 调试
        // 从当前玩家所在的位置发射火球
        let x = this.x;
        let y = this.y;
        // 设置火球半径
        // 将绝对坐标转为相对坐标
        let radius = this.playground.height * 0.01 / this.playground.scale;
        // 求出速度的角度
        let angle = Math.atan2(ty - y, tx - x);
        // 求出速度的方向
        let vx = 1 * Math.cos(angle);
        let vy = 1 * Math.sin(angle);
        // 设置火球颜色
        let color = "orange";
        // 设置火球速度
        // 将绝对坐标转为相对坐标
        let speed = this.playground.height * 0.5 / this.playground.scale;
        // 设置火球移动的距离
        // 将绝对坐标转为相对坐标
        let move_length = this.playground.height * 1 / this.playground.scale;
        // 设置火球伤害
        // 由于玩家的大小为0.05倍屏幕高度
        // 那么当伤害为0.01倍屏幕高度时
        // 每次攻击可以使玩家掉20%的血量
        // 将绝对坐标转为相对坐标
        let damage = this.playground.height * 0.01 / this.playground.scale;
        // 创建火球，将上述所有参数传入
        let fireball = new FireBall(this.playground, this, x, y, radius, vx, vy, color, speed, move_length, damage);
        this.fireballs.push(fireball);

        this.fireball_coldtime = 3;

        return fireball;
    }

    destroy_fireball(uuid) {
        for (let i = 0; i < this.fireballs.length; i ++) {
            let fireball = this.fireballs[i];
            if (fireball.uuid === uuid) {
                fireball.destroy();
                break;
            }
        }
    }

    blink(tx, ty) {
        let d = this.get_dist(this.x, this.y, tx, ty);
        d = Math.min(d, 0.8);
        let angle = Math.atan2(ty - this.y, tx - this.x);
        this.x += d * Math.cos(angle);
        this.y += d * Math.sin(angle);

        this.blink_coldtime = 5;
        this.move_length = 0;   // 闪现结束停下来
    }

    // 计算两点之间的距离
    get_dist(x1, y1, x2, y2) {
        let dx = x1 - x2;
        let dy = y1 - y2;
        return Math.sqrt(dx * dx + dy * dy);
    }

    // 求出速度方向后，
    // 物体会在update中进行移动
    move_to(tx, ty) {
        // 求出移动的距离
        this.move_length = this.get_dist(this.x, this.y, tx, ty);
        // 求出速度的角度
        let angle = Math.atan2(ty - this.y, tx - this.x);
        // 求出速度的方向
        // 速度方向为vx和vy，归一化到单位圆上
        this.vx = 1 * Math.cos(angle);
        this.vy = 1 * Math.sin(angle);
    }

    // 如果物体被攻击
    is_attacked(angle, damage) {
        // 这里是粒子效果
        // 在玩家被攻击的时候需要触发
        for (let i = 0; i < 20 + Math.random() * 10; i ++) {    // 随机粒子的数量
            // 粒子出现的位置
            let x = this.x;
            let y = this.y;
            // 粒子的半径
            let radius = this.radius * Math.random() * 0.1;
            // 粒子发散的角度
            let angle = Math.random() * Math.PI * 2;
            // 将角度转换为速度方向
            let vx = Math.cos(angle);
            let vy = Math.sin(angle);
            // 粒子的颜色
            let color = this.color;
            // 粒子的速度
            let speed = this.speed * 10;
            // 粒子的移动距离
            let move_length = this.radius * Math.random() * 5;
            // 创建粒子
            new Particle(this.playground, x, y, radius, vx, vy, color, speed, move_length);
        }

        // 玩家被攻击后，需要减少血量
        // 这里用半径来表示血量
        this.radius -= damage;
        // 如果血量（半径）小于10，则玩家死亡
        if (this.radius < this.eps) {
            this.destroy();
            return false;
        }
        // 如果玩家被攻击
        // 玩家会根据攻击的方向后退一段距离
        // 需要设置方向和速度
        // 方向
        this.damage_x = Math.cos(angle);
        this.damage_y = Math.sin(angle);
        // 速度
        this.damage_speed = damage * 200;
        // 玩家被攻击后，速度会变快
        this.speed *= 1.2;
    }

    receive_attack(x, y, angle, damage, ball_uuid, attacker) {
        attacker.destroy_fireball(ball_uuid);
        this.x = x;
        this.y = y;
        this.is_attacked(angle, damage);
    }

    // 更新
    update() {
        // 计算已经经过的时间
        this.spent_time += this.timedelta / 1000;

        if (this.character === "me" && this.playground.state === "fighting") {
            this.update_coldtime();
        }
        this.update_move();

        this.render();
    }

    update_coldtime() {
        this.fireball_coldtime -= this.timedelta / 1000;
        this.fireball_coldtime = Math.max(this.fireball_coldtime, 0);

        this.blink_coldtime -= this.timedelta / 1000;
        this.blink_coldtime = Math.max(this.blink_coldtime, 0);
    }

    // 只负责更新玩家移动
    update_move() {
        // 如果不是自己并且已经经过了4秒并且满足概率并且场上的玩家数大于1
        if (this.character === "robot" && this.spent_time > 4 && Math.random() < 1 / 300.0 && this.playground.players.length > 1) {
            // AI会随机选择一个玩家进行攻击
            let player = this.playground.players[Math.floor(Math.random() * this.playground.players.length)];
            // 并且在这里会进行预判
            let tx = player.x + player.speed * this.vx * this.timedelta / 1000 * 0.3;
            let ty = player.y + player.speed * this.vy * this.timedelta / 1000 * 0.3;
            // 这里会进行攻击
            this.shoot_fireball(tx, ty);
        }
        // 如果受到攻击，那么会硬直并且根据攻击的方向后退
        // 所以需要将移动相关的参数清零
        if (this.damage_speed > this.eps) {
            this.vx = 0;
            this.vy = 0;
            this.move_length = 0;
            // 硬直的时候，会根据攻击的方向后退
            this.x += this.damage_x * this.damage_speed * this.timedelta / 1000;
            this.y += this.damage_y * this.damage_speed * this.timedelta / 1000;
            // 后退的速度会逐渐减小
            this.damage_speed *= this.friction;
        } else {    // 这里是没有受到攻击的时候
            // 如果移动的距离小于eps，那么就认为移动结束
            if (this.move_length < this.eps) {
                // 将移动的距离清零
                this.move_length = 0;
                // 将速度清零
                this.vx = 0;
                this.vy = 0;
                // 如果不是自己，那么就随机选择一个位置进行移动
                if (this.character === "robot") {
                    let tx = Math.random() * this.playground.width / this.playground.scale;
                    let ty = Math.random() * this.playground.height / this.playground.scale;
                    this.move_to(tx, ty);
                }
            } else {    // 如果移动还没有结束
                // 移动的距离要么是speed * 时间，要么是剩下的距离
                let moved = Math.min(this.move_length, this.speed * this.timedelta / 1000);
                // 更新坐标
                // 将x和y坐标分别加上移动距离在x和y方向上的分量
                this.x += this.vx * moved;
                this.y += this.vy * moved;
                this.move_length -= moved;
            }
        }
    }

    // 将玩家绘制到地图上，就是画圆
    render() {
        let scale = this.playground.scale;
        if (this.character !== "robot") {
            this.ctx.save();
            this.ctx.beginPath();
            this.ctx.arc(this.x * scale, this.y * scale, this.radius * scale, 0, Math.PI * 2, false);
            this.ctx.stroke();
            this.ctx.clip();
            this.ctx.drawImage(this.img, (this.x - this.radius) * scale, (this.y - this.radius) * scale, this.radius * 2 * scale, this.radius * 2 * scale);
            this.ctx.restore();
        } else {
            this.ctx.beginPath();
            // (坐标x，坐标y，半径，起始角度，结束角度)
            this.ctx.arc(this.x * scale, this.y * scale, this.radius * scale, 0, 2 * Math.PI, false);
            // 设置填充颜色
            this.ctx.fillStyle = this.color;
            this.ctx.fill();
        }

        if (this.character === "me" && this.playground.state === "fighting") {
            this.render_skill_coldtime();
        }
    }

    render_skill_coldtime() {
        let scale = this.playground.scale;
        let x = 1.5;
        let y = 0.9;
        let r = 0.04;

        this.ctx.save();
        this.ctx.beginPath();
        this.ctx.arc(x * scale, y * scale, r * scale, 0, Math.PI * 2, false);
        this.ctx.stroke();
        this.ctx.clip();
        this.ctx.drawImage(this.fireball_img, (x - r) * scale, (y - r) * scale, r * 2 * scale, r * 2 * scale);
        this.ctx.restore();

        if (this.fireball_coldtime > 0) {
            this.ctx.beginPath();
            this.ctx.moveTo(x * scale, y * scale);
            this.ctx.arc(x * scale, y * scale, r * scale, 0 - Math.PI / 2, 2 * Math.PI * (1 - this.fireball_coldtime / 3) - Math.PI / 2, true);
            this.ctx.lineTo(x * scale, y * scale);
            this.ctx.fillStyle = "rgba(0, 0, 255, 0.6)";
            this.ctx.fill();
        }

        x = 1.62;
        this.ctx.save();
        this.ctx.beginPath();
        this.ctx.arc(x * scale, y * scale, r * scale, 0, Math.PI * 2, false);
        this.ctx.stroke();
        this.ctx.clip();
        this.ctx.drawImage(this.blink_img, (x - r) * scale, (y - r) * scale, r * 2 * scale, r * 2 * scale);
        this.ctx.restore();

        if (this.blink_coldtime > 0) {
            this.ctx.beginPath();
            this.ctx.moveTo(x * scale, y * scale);
            this.ctx.arc(x * scale, y * scale, r * scale, 0 - Math.PI / 2, 2 * Math.PI * (1 - this.blink_coldtime / 5) - Math.PI / 2, true);
            this.ctx.lineTo(x * scale, y * scale);
            this.ctx.fillStyle = "rgba(0, 0, 255, 0.6)";
            this.ctx.fill();
        }
    }

    // 销毁玩家时的动作
    on_destroy() {
        if (this.character === "me")
            this.playground.state = "over";

        for (let i = 0; i < this.playground.players.length; i ++) {
            if (this.playground.players[i] === this) {
                this.playground.players.splice(i, 1);
                break;
            }
        }
    }
}
